package cn.com.jcoo.ehcahce;


import cn.com.jcoo.config.EhcacheProperties;
import cn.com.jcoo.config.SaTokenCacheProperties;
import lombok.extern.slf4j.Slf4j;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import net.sf.ehcache.config.CacheConfiguration;
import net.sf.ehcache.config.Configuration;
import net.sf.ehcache.config.DiskStoreConfiguration;
import org.springframework.beans.factory.DisposableBean;

import java.io.File;
import java.util.Objects;

@Slf4j
@SuppressWarnings("unused")
public class SaTokenEhcacheManager implements DisposableBean {
    private final EhcacheProperties ehcacheProperties;
    private CacheManager cacheManager;

    public SaTokenEhcacheManager(SaTokenCacheProperties saTokenCacheProperties) {
        this.ehcacheProperties = saTokenCacheProperties.getEhcache();
        init(ehcacheProperties);
        log.info("init SaTokenEhcacheManager, diskPersistent:{}", ehcacheProperties.isDiskPersistent());
    }


    public void init(EhcacheProperties ehcacheProperties) {
        // 自定义缓存管理器配置
        Configuration exampleManagerConfig = new Configuration()
                // 缓存管理器名称
                .name(ehcacheProperties.getName())
                // 磁盘存储位置配置
                .diskStore(new DiskStoreConfiguration().path(ehcacheProperties.getDiskStorePath() + File.separator + ehcacheProperties.getName()));
        // 缓存管理器中缓存的默认配置
        CacheConfiguration configuration = new CacheConfiguration()
                // 1.可用内存配置
                // - 指定虚拟机堆元素个数限制， 0-无限制
                .maxEntriesLocalHeap(ehcacheProperties.getMaxEntriesLocalHeap())
                // 2. 过期策略配置
                // 元素永不过期:false
                .eternal(ehcacheProperties.isEternal())
                // 过期策略
                .memoryStoreEvictionPolicy(ehcacheProperties.getMemoryStoreEvictionPolicy())
                // 如果仅有timeToLiveSeconds那么自创建时间开始 间隔x后缓存失效；
                // 如果没有timeToLiveSeconds那么自最后一次访问缓存 间隔y后 缓存失效；
                // 如果既有timeToLiveSeconds也有timeToIdleSeconds那么取最小数算作间隔时间；min(x,y);
                // 元素自创建日期起能够存活的最长时间，单位为秒(s)，0-无限制
                .timeToLiveSeconds(ehcacheProperties.getTimeToLiveSeconds())
                // 元素被创建后，最后一次访问时间到缓存失效之时，两者之间的间隔，单位为秒(s)
                .timeToIdleSeconds(ehcacheProperties.getTimeToIdleSeconds())
                // 3. 磁盘及持久化策略配置
                // 设置磁盘后台处理程序大小的生成器，用于缓冲对 DiskStore 的写入。默认是30MB。
                .diskSpoolBufferSizeMB(20)
                // 用于设置当内存中缓存达到设定的限制时元素是否可以溢出到磁盘。
                .overflowToDisk(ehcacheProperties.isOverflowToDisk())
                // 用于设置磁盘存储是否在 CacheManager 实例之间保留。请注意，这独立于 overflowToDisk.
                .diskPersistent(ehcacheProperties.isDiskPersistent())
                // 测试发现 使用下面方法程序关闭后会删除磁盘文件
//                .persistence(new PersistenceConfiguration()
//                        .strategy(PersistenceConfiguration.Strategy.LOCALTEMPSWAP)
//                )
                // 可用磁盘存储元素个数限制
                .maxEntriesLocalDisk(ehcacheProperties.getMaxEntriesLocalDisk())
                // 缓存事件监听
//                .cacheEventListenerFactory()
                ;
        exampleManagerConfig.defaultCache(configuration);
        // 创建单一实例 CacheManager
        cacheManager = CacheManager.create(exampleManagerConfig);
        // 添加默认缓存
        cacheManager.addCacheIfAbsent(ehcacheProperties.getDefaultCacheName());
    }


    public Cache getCache() {
        return getCache(ehcacheProperties.getDefaultCacheName());
    }


    public Cache getCache(String cacheName) {
        cacheManager.addCacheIfAbsent(cacheName);
        return cacheManager.getCache(cacheName);
    }

    public <V> void set(Cache cache, String key, V value, int timeout) {
        Element element = new Element(key, value, timeout, timeout);
        cache.put(element);
    }

    public Object get(Cache cache, String key) {
        Element element = cache.get(key);
        if (element == null) {
            return null;
        }
        return element.getObjectValue();
    }

    public String getStr(Cache cache, String key) {
        Object value = get(cache, key);
        return Objects.isNull(value) ? "" : value.toString();
    }

    public long getKeyExpiration(Cache cache, String key) {
        Element element = cache.get(key);

        if (element == null) {
            return 0;
        }
        long expirationTime = element.getExpirationTime();
        long currentTimeMillis = System.currentTimeMillis();
        return (expirationTime - currentTimeMillis) / 1000;
    }

    public Object removeKey(Cache cache, String key) {
        return cache.remove(key);
    }

    public <V> void set(String key, V value, int timeout) {
        set(getCache(), key, value, timeout);
    }

    public Object get(String key) {
        return get(getCache(), key);
    }

    public String getStr(String key) {
        return getStr(getCache(), key);
    }


    public long getKeyExpiration(String key) {
        return getKeyExpiration(getCache(), key);
    }


    public Object removeKey(String key) {
        return removeKey(getCache(), key);
    }

    /**
     * 关闭实例
     */
    public void shutdown() {
        CacheManager.getInstance().shutdown();
    }

    @Override
    public void destroy() throws Exception {
        this.shutdown();
    }
}
